using System;
using System.Collections.Generic;
using System.Linq;


namespace Utilities
{
  public class ArrayList<T> : IEnumerable<T>
  {
    T[] data;
    int occupied = 0;
    int capacity;
    public ArrayList() : this(25) { capacity = 25; }
    public ArrayList(int capacity)
    {
      data = new T[capacity]; 
      this.capacity = capacity;
    }
    public ArrayList(IEnumerable<T> source)
      : this(source.Count())
    {
      //for_each (var item in source) 
      //    this.Add(item);
      for (int i = 0; i < source.Count(); i++)
        this.Add(source.ElementAt(i));
    }

    public void Add(T t)
    {
      if (occupied >= data.Length)
      {
        var prev_data = data;
        data = new T[data.Length * 2 + 10];
        for (int i = 0; i < prev_data.Length; i++)
        {
          data[i] = prev_data[i];
        }
      }
      data[occupied++] = t;
    }

    public void RemoveAt(int i)
    {
      data[i] = data[--occupied];
    }

    public void Remove(T t)
    {
      for (int i = 0; i < occupied; i++)
      {
        if (data[i].Equals(t))
        {
          RemoveAt(i);
          return;
        }
      }
    }

    public void Clear()
    {
      occupied = 0;
      if (data.Length > 1000)
        data = new T[capacity];
    }

    public T this[int index]
    {
      get { return data[index]; }
      set { data[index] = value; }
    }

    public int Count { get { return occupied; } }
    public int Length { get { return capacity; } }
    struct ArrayListEnumerator : IEnumerator<T>
    {
      internal int current;
      internal ArrayList<T> source;

      public T Current
      {
        get { return source[current]; }
      }

      public void Dispose()
      {
        return;
      }

      object System.Collections.IEnumerator.Current
      {
        get { return source[current]; }
      }

      public bool MoveNext()
      {
        current++;
        return current < source.Count;
      }

      public void Reset()
      {
        current = 0;
      }
    }

    public IEnumerator<T> GetEnumerator()
    {
      return new ArrayListEnumerator() { current = 0, source = this };
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
      return new ArrayListEnumerator() { current = 0, source = this };
    }

    public int IndexOf(T t)
    {
      for (int i = 0; i < occupied; i++)
      {
        if (data[i].Equals(t))
          return i;
      }
      return -1;
    }

    public bool Contains(T t)
    {
      return IndexOf(t) != -1;
    }
  }

  public class ArrayStack<T> : ArrayList<T>
  {
    public ArrayStack(int capacity) : base(capacity) { }
    public void Push(T t) { base.Add(t); }
    public T Pop()
    {
      if (base.Count > 0)
        base.RemoveAt(base.Count - 1);
      return base[base.Count];
    }
  }

  public interface IActivable
  {
    bool IsActive { get; set; }
  }

  public class Chunk<T> where T : IActivable
  {
    public Chunk(int index)
    {
      Items = new T[StructAllocator<T>.ChunkSize];
      NumFreeItems = StructAllocator<T>.ChunkSize;
      Index = index;
    }
    public int Index;
    public T[] Items;
    public int NumFreeItems;
  }

  public struct Reference : IEquatable<Reference>
  {
    public int ChunkIndex; public int ItemIndex;

    public bool Equals(Reference other)
    {
      return ChunkIndex == other.ChunkIndex && ItemIndex == other.ItemIndex;
    }
  }

  public static class StructAllocatorManager
  {
    static internal List<Action> struct_allocators_reset = new List<Action>();
    //static public void Reset() { struct_allocators_reset.For_Each(a => a()); }
    static public void Reset()
    {
      //struct_allocators_reset.For_Each(a => a());
      for (int i = 0; i < struct_allocators_reset.Count; i++)
        struct_allocators_reset[i]();
    }
  }

  public static class StructAllocator<T> where T : IActivable
  {
    static public readonly int ChunkSize = 100;
    static public void Reset() { Chunks = Enumerable.Range(0, 20).Select(i => new Chunk<T>(i)).ToList(); }

    static private Func<bool> start = () => { StructAllocatorManager.struct_allocators_reset.Add(StructAllocator<T>.Reset); return true; };
    static private bool started = start();

    static public List<Chunk<T>> Chunks = Enumerable.Range(0, 20).Select(i => new Chunk<T>(i)).ToList();

    static private List<Action<Reference>> OnEnable = new List<Action<Reference>>();
    static private List<Action<Reference>> OnDisable = new List<Action<Reference>>();
    static public void Link(Action<Reference> on_enable, Action<Reference> on_disable)
    {
      OnEnable.Add(on_enable);
      OnDisable.Add(on_disable);
    }

    static public Reference New()
    {
      //var chunk = Chunks.FirstOrDefault(c => c.NumFreeItems > 0);
      Chunk<T> chunk = null;
      for (int i = 0; i < Chunks.Count; i++)
      {
          if (Chunks[i].NumFreeItems > 0)
          {
              chunk = Chunks[i];
              break;
          }
      }
      if (chunk == null)
      {
        Chunks.Add(new Chunk<T>(Chunks.Count));
        return New();
      }
      chunk.NumFreeItems--;
      int item_index = 0;
      for (; item_index < chunk.Items.Length; item_index++)
      {
        if (chunk.Items[item_index].IsActive == false)
          break;
      }
      chunk.Items[item_index].IsActive = true;
      var chunk_index = chunk.Index;
      var res = new Reference() { ChunkIndex = chunk_index, ItemIndex = item_index };
      for (int i = 0; i < OnEnable.Count; i++)
      {
        OnEnable[i](res);
      }
      return res;
    }

    static public int UsedItems
    {
      get { return Chunks.Sum(c => c.Items.Count(i => i.IsActive)); }
    }

    static public bool FreeItemsOk
    {
      get { return Chunks.All(c => c.Items.Count(i => i.IsActive == false) == c.NumFreeItems); }
    }

    static public void Delete(Reference item)
    {
      var chunk = Chunks[item.ChunkIndex];
      chunk.NumFreeItems++;
      chunk.Items[item.ItemIndex].IsActive = false;
      for (int i = 0; i < OnDisable.Count; i++)
      {
        OnDisable[i](item);
      }
    }

    static public void Enable(Reference item)
    {
      while (Chunks.Count <= item.ChunkIndex) Chunks.Add(new Chunk<T>(Chunks.Count));
      Chunks[item.ChunkIndex].Items[item.ItemIndex].IsActive = true;
    }

    static public void Disable(Reference item)
    {
      Chunks[item.ChunkIndex].Items[item.ItemIndex].IsActive = false;
    }
  }
}
